﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Koala.Pro.Common.Extension
{
    public static class EnumerableExtensions
    {
        public static IEnumerable<T> Assert<T>(this IEnumerable<T> source, Func<T, bool> predicate, Func<T, Exception> errorSelector = null)
        {
            foreach (T item in source)
            {
                if (!predicate(item))
                {
                    throw errorSelector?.Invoke(item) ?? new InvalidOperationException("Sequence contains an invalid item.");
                }
                yield return item;
            }
        }

        public static bool EndsWith<T>(this IEnumerable<T> first, IEnumerable<T> second)
        {
            return first.EndsWith(second, null);
        }

        public static bool EndsWith<T>(this IEnumerable<T> first, IEnumerable<T> second, IEqualityComparer<T> comparer)
        {
            if (first == null)
            {
                throw new ArgumentNullException("first");
            }
            if (second == null)
            {
                throw new ArgumentNullException("second");
            }
            comparer = comparer ?? EqualityComparer<T>.Default;
            int? num = second.TryGetCollectionCount();
            if (num.HasValue)
            {
                int? num2 = first.TryGetCollectionCount();
                if (num2.HasValue && num > num2)
                {
                    return false;
                }
                return Impl(second, num.Value);
            }
            List<T> list;
            return Impl(list = second.ToList(), list.Count);
            bool Impl(IEnumerable<T> snd, int count)
            {
                IEnumerator<T> firstIter = first.Reverse().Take(count).Reverse()
                    .GetEnumerator();
                try
                {
                    return snd.All((T item) => firstIter.MoveNext() && comparer.Equals(firstIter.Current, item));
                }
                finally
                {
                    if (firstIter != null)
                    {
                        firstIter.Dispose();
                    }
                }
            }
        }

        public static IEnumerable<TSource> Shuffle<TSource>(this IEnumerable<TSource> source)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            return source.OrderBy((TSource m) => Guid.NewGuid());
        }

        public static string ExpandAndToString<T>(this IEnumerable<T> collection, string separator = ",")
        {
            return collection.ExpandAndToString(delegate (T item)
            {
                object obj = item?.ToString();
                if (obj == null)
                {
                    obj = string.Empty;
                }
                return (string)obj;
            }, separator);
        }

        public static string ExpandAndToString<T>(this IEnumerable<T> collection, Func<T, string> itemFormatFunc, string separator = ",")
        {
            collection = (collection as IList<T>) ?? collection.ToList();
            itemFormatFunc.CheckNotNull("itemFormatFunc");
            if (!collection.Any())
            {
                return string.Empty;
            }
            StringBuilder stringBuilder = new StringBuilder();
            int num = 0;
            int num2 = collection.Count();
            foreach (T item in collection)
            {
                if (num == num2 - 1)
                {
                    stringBuilder.Append(itemFormatFunc(item));
                }
                else
                {
                    stringBuilder.Append(itemFormatFunc(item) + separator);
                }
                num++;
            }
            return stringBuilder.ToString();
        }

        public static bool IsEmpty<T>(this IEnumerable<T> collection)
        {
            collection = (collection as IList<T>) ?? collection.ToList();
            return !collection.Any();
        }

        public static IEnumerable<T> WhereIf<T>(this IEnumerable<T> source, Func<T, bool> predicate, bool condition)
        {
            predicate.CheckNotNull("predicate");
            source = (source as IList<T>) ?? source.ToList();
            if (!condition)
            {
                return source;
            }
            return source.Where(predicate);
        }

        public static IEnumerable<T> OrderByPrefixes<T>(this IEnumerable<T> source, Func<T, string> keySelector, params string[] prefixes)
        {
            List<T> list = source.OrderBy(keySelector).ToList();
            List<T> list2 = new List<T>();
            foreach (string prefix in prefixes)
            {
                List<T> list3 = list.Where((T m) => keySelector(m).StartsWith(prefix)).OrderBy(keySelector).ToList();
                list = list.Except(list3).ToList();
                list2.AddRange(list3);
            }
            list2.AddRange(list);
            return list2;
        }

        public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector)
        {
            keySelector.CheckNotNull("keySelector");
            source = (source as IList<T>) ?? source.ToList();
            return from @group in source.GroupBy(keySelector)
                   select @group.First();
        }

        internal static int? TryGetCollectionCount<T>(this IEnumerable<T> source)
        {
            if (source != null)
            {
                if (!(source is ICollection<T> collection))
                {
                    if (source is IReadOnlyCollection<T> readOnlyCollection)
                    {
                        return readOnlyCollection.Count;
                    }
                    return null;
                }
                return collection.Count;
            }
            throw new ArgumentNullException("source");
        }

        private static int CountUpTo<T>(this IEnumerable<T> source, int max)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            if (max < 0)
            {
                throw new ArgumentOutOfRangeException("max", "The maximum count argument cannot be negative.");
            }
            int i = 0;
            using (IEnumerator<T> enumerator = source.GetEnumerator())
            {
                for (; i < max; i++)
                {
                    if (!enumerator.MoveNext())
                    {
                        break;
                    }
                }
            }
            return i;
        }
    }

}
